El dataset contiene 515k filas de reseñas de usuarios paa 1493 hoteles de Europa. Este dataset contiene los siguientes atributos:
-Hotel_Address: Dirección del Hotel -Review_Date: Fecha de cuando se realizo la Review. -Average_Score: Puntuación media del Hotel. -Hotel_Name: Nombre del hotel -Reviewer_Nationality: Nacionalidad del usuario que deja la reseña -Negative_Review: Comentarios negativos que el usuario ha dejado al hotel. Si no se pone ningun comentario negativo debe de ser: ‘No Negative’ -Review_Total_Negative_Word_Counts: Numero total de palabras en la review Negativa. -Positive_Review: Comentarios positivos que el usuario ha dejado al hotel. si no se pone ningun comentario positivo debe de ser: ‘No Positive’ -Review_Total_Positive_Word_Counts: Numero total de palabras en la review Positiva. -Reviewer_Score: Puntuacion que le ha dejado el usuario en la reseña al hotel. -Total_Number_of_Reviews_Reviewer_Has_Given: Numero de reseñas que ha hecho el usuario en el pasado. -Total_Number_of_Reviews: Numero total de reviews validas del hotel. -Tags: Etiquetas que el usuario le ha dejado al hotel. -days_since_review: Dias que han pasado desde que salio del hotel hasta que se hizo la reseña. -Additional_Number_of_Scoring:Hay algunos huespedes que hacen una puntuacion al servicio en vez de una reseña. Este numero indica cuantas puntuaciones son validad sin review. -lat: Latitud del hotel (geo) -lng: Longitud del hotel (geo)
library(sqldf)
library(plotly)
library(leaflet)
library(leaflet.extras)
library(ggplot2)
library(wordcloud2)
library(tm)
library(dplyr)
library(readr)
library(stringr)
library(broom)
library(udpipe)
library(tidytext)
library(tidyr)
library(gridExtra)
library(topicmodels)
df <- read_csv('./Datasets/Hotel_Reviews.csv')
#tratamos los datos para extraer el año de cada review en una nueva columna
df <- df %>%
mutate(Review_year = substr(as.character(Review_Date),
nchar(as.character(Review_Date))-3,
nchar(as.character(Review_Date))))
#Añade una columa nueva a el data frame quitando los caracteres que no sean un numero sobre la columna data since review Ej: "3 days" -> 3
df <- mutate(df, days_since_review_t= as.numeric(sub('[^0-9]+',"",days_since_review )))
head(df)
## # A tibble: 6 x 19
## Hotel_Address Additional_Numb… Review_Date Average_Score Hotel_Name
## <chr> <dbl> <chr> <dbl> <chr>
## 1 s Gravesande… 194 8/3/2017 7.7 Hotel Are…
## 2 s Gravesande… 194 8/3/2017 7.7 Hotel Are…
## 3 s Gravesande… 194 7/31/2017 7.7 Hotel Are…
## 4 s Gravesande… 194 7/31/2017 7.7 Hotel Are…
## 5 s Gravesande… 194 7/24/2017 7.7 Hotel Are…
## 6 s Gravesande… 194 7/24/2017 7.7 Hotel Are…
## # … with 14 more variables: Reviewer_Nationality <chr>, Negative_Review <chr>,
## # Review_Total_Negative_Word_Counts <dbl>, Total_Number_of_Reviews <dbl>,
## # Positive_Review <chr>, Review_Total_Positive_Word_Counts <dbl>,
## # Total_Number_of_Reviews_Reviewer_Has_Given <dbl>, Reviewer_Score <dbl>,
## # Tags <chr>, days_since_review <chr>, lat <dbl>, lng <dbl>,
## # Review_year <chr>, days_since_review_t <dbl>
Se muestran los hoteles de lujo mas visitados por los turistas a partir de las reviews realizadas en las ciudades mas visitadas en el ranking anual (Barcelona, Paris, Londes…)
location <- df[!duplicated(df[,c('lat','lng','Hotel_Address')]),c('lat','lng','Hotel_Address','Average_Score')]
leaflet(data = location)%>%addProviderTiles(providers$Stamen.TonerLite)%>%addMarkers(popup = ~Hotel_Address,clusterOptions = markerClusterOptions())
Muestra el mismo resultado que el anterior pero con el mapa a color, para su posible exportación a una aplicacion.
leaflet(data = location)%>%addProviderTiles(providers$Esri.NatGeoWorldMap)%>%addMarkers(popup = ~Hotel_Address)
## Añadiendo campo pais y ciudad a nuestro dataframe Este campo se calcula a partir del campo Hotel_Adress y se añade como nuevas columnas country
library(stringr)
df%>%select(Hotel_Name,lat,lng,Hotel_Address)%>%group_by(Hotel_Address)%>%filter(!duplicated(Hotel_Address))->hotel_details
hotel_details$country=sapply(str_split(hotel_details$Hotel_Address," "),function(x){x[length(x)]})
hotel_details$city=sapply(str_split(hotel_details$Hotel_Address," "),function(x){x[length(x)-1]})
## Remove the mention of "United" as "London" in the city column and "Kingdom" as "United Kingdom" in the country column
hotel_details$city=str_replace(hotel_details$city,"United","London")
hotel_details$country=str_replace(hotel_details$country,"Kingdom","United Kingdom")
df%>%left_join(hotel_details[,4:6],by = 'Hotel_Address')->df
countries=paste(unique(hotel_details$country),collapse=",")
message=paste("Los paises mencionados en el dataset son:", countries)
print(message)
[1] “Los paises mencionados en el dataset son: Netherlands,United Kingdom,France,Spain,Italy,Austria”
cities=paste(unique(hotel_details$city),collapse=",")
message=paste("Las ciudades mencionadas en el dataset son:", cities)
print(message)
[1] “Las ciudades mencionadas en el dataset son: Amsterdam,London,Paris,Barcelona,Milan,Vienna” ## Puntuacion media por Paises Se aprecia que Austria es el que tiene un rango menor de puntuaciones y es el que tendria las puntuaciones más altas de media.
df%>%ggplot(aes(x=as.factor(country),y=Average_Score))+geom_boxplot()+xlab("País")+ylab("Puntuación media")
## Puntuacion media segun si son extranjeros o no Se añade al dataset el campo turista, que si representa si es un extrangero o no.
Con esto observamos que los que no son Turistas dejan una puntuación mayor que los que si lo son, a excepcion de Inglaterra
ind<-which(is.na(df$Reviewer_Nationality))
#print(ind)
data_model<-df[-ind,]
#data_model<-df
#print(data_model$Reviewer_Nationality==data_model$country)
data_model$turista<-ifelse(data_model$Reviewer_Nationality==data_model$country,"Sí","No")
data_model$turista<-as.factor(data_model$turista)
#print(data_model)
data_model%>%group_by(country,turista)%>%summarise(average_score=mean(Average_Score))%>%ungroup()%>%mutate(average_score=average_score**7)%>%ggplot(aes(x=country,y=average_score,color=turista,fill=turista))+geom_bar(stat='identity',position='dodge')+xlab("País")+ylab("Puntuación media")+scale_y_continuous(breaks = NULL)
## Las puntuaciones medias dadas a hoteles de Barcelona segun una muestra de nacionalidaddes
data_model%>%filter(city=='Barcelona',Reviewer_Nationality %in% c("United States of America","Australia","Ireland","United Arab Emirates","Saudi Arabia"))%>%ggplot(aes(x=Reviewer_Nationality,y=Average_Score))+geom_boxplot()+xlab("País")+ylab("Puntuación media")
La distribucion de las puntuaciones dependiendo si las reviews son de turistas de fuera o de Barcelona.
data_model%>%filter(city=='Barcelona',Reviewer_Nationality!='Spain')%>%ggplot(aes(x=Average_Score))+geom_histogram(alpha=0.3,color='blue',fill='blue')+xlab("Puntuación media")+ylab("Contador")+ggtitle("Distribución de puntos \n (Turistas)")+scale_x_continuous(limits = c(6,10))+theme(plot.title = element_text(size=16,hjust=0.5))->t
data_model%>%filter(city=='Barcelona',Reviewer_Nationality=='Spain')%>%ggplot(aes(x=Average_Score))+geom_histogram(alpha=0.3,color='red',fill='red')+xlab("Puntuación media")+ylab("Contador")+ggtitle("Distribución de puntos \n (Locales)")+scale_x_continuous(limits=c(6,10))+theme(plot.title = element_text(size=16,hjust=0.5))->l
grid.arrange(t,l,ncol=2)
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
Se muestran el numero de usuarios que han dado una cierta puntuación.
g <- ggplot(df[sample(nrow(df), 10000), ],aes(x=Reviewer_Score)) + geom_histogram(binwidth = 1)+theme_bw()+ggtitle('Distribución de las puntuaciones')+xlab("Puntuación del cliente")+ylab("Contador")
ggplotly(g)
Las nacionalidaddes de usuarios que dan las mayores puntuaciones.
Modificacion ordenamos las nacionalidades por orden de puntuación media descendiente. Hecho con las funcion reorder en el eje x.
avgscore_nation <- sqldf('SELECT Reviewer_Nationality, avg(Reviewer_Score) as avg_score from df group by Reviewer_Nationality order by avg(Reviewer_Score) desc')
avgscore_nation[166,1]<-'UnKnown'
g <- ggplot(avgscore_nation[1:20,],aes(x=reorder( Reviewer_Nationality, -avg_score),y=avg_score)) + geom_bar(stat = 'identity')+theme_bw()+ theme(axis.text.x = element_text(angle = 45, hjust = 1)) + ggtitle('TOP-20 de las nacionalidades con puntuación media más alta')+ylab("Puntuación media")+xlab("Nacionalidad")
ggplotly(g)
Las nacionalidaddes de usuarios que dan las peores puntuaciones.
#TODO - Ordenar por puntuaciones - DONE Modificacion ordenamos las nacionalidades por orden de puntuación media ascendiente Hecho con las funcion reorder en el eje x.
g <- ggplot(avgscore_nation[207:227,],aes(x=reorder( Reviewer_Nationality, avg_score),y=avg_score)) + geom_bar(stat = 'identity')+theme_bw()+ theme(axis.text.x = element_text(angle = 45, hjust = 1)) + ggtitle('TOP-20 de las nacionalidades con las puntuaciones medias más bajas')+ylab("Puntuación media")+xlab("Nacionalidad")
ggplotly(g)
Muestra la correlacion entre la puntuacion y los dias que han pasado desde que dejo el hotel hasta hacer la review.
#Añade una columa nueva a el data frame quitando los caracteres que no sean un numero sobre la columna data since review Ej: "3 days" -> 3
#df <- mutate(df, days_since_review_t= as.numeric(sub('[^0-9]+',"",days_since_review )))
#print(df)
g <- ggplot(df[sample(nrow(df), 10000), ],aes(x=days_since_review_t,y=Reviewer_Score)) + geom_point()+theme_bw()+geom_smooth(method = "lm")+ggtitle('Correlación entre la puntuación y los dias desde estancia')+ylab("Puntuación del cliente")+xlab("Días transcurridos desde estancia")
ggplotly(g)
Se puede comprobar que no existe relacion distinguible entre las puntuaciones que dejan los usuarios y los dias transcurridos desde que abandonó el hotel. Todos dejan una media aproximada de {8-9} puntos.
avgscore_days <- sqldf('SELECT rango_dias, avg(Reviewer_Score) as avg_score
from (
select case
when days_since_review_t between 0 and 50 then \'a) 0 a 50\'
when days_since_review_t between 51 and 100 then \'b) 51 a 100\'
when days_since_review_t between 101 and 150 then \'c) 101 a 150\'
when days_since_review_t between 151 and 200 then \'d) 151 a 200\'
when days_since_review_t between 201 and 250 then \'e) 201 a 250\'
when days_since_review_t between 251 and 300 then \'f) 251 a 300\'
when days_since_review_t between 301 and 350 then \'g) 301 a 350\'
when days_since_review_t between 351 and 400 then \'h) 351 a 400\'
else \'i) mas de 400\' end as rango_dias,
Reviewer_Score
from df)
group by rango_dias order by rango_dias desc')
g <- ggplot(avgscore_days,aes(x=rango_dias,y=avg_score)) + geom_bar(stat = 'identity')+theme_bw()+ theme(axis.text.x = element_text(angle = 45, hjust = 1)) + ggtitle('Relación entre las reviews y los dias transcurridos.')+ylab("Contador")
ggplotly(g)
### Relationship between frequency of review and score
g <- ggplot(df[sample(nrow(df), 10000), ],aes(x=Total_Number_of_Reviews_Reviewer_Has_Given,y=Reviewer_Score)) + geom_point()+theme_bw()+geom_smooth(method = "lm")+ggtitle('Correlation between score and review frequency')
ggplotly(g)
Se muestran las palabras mas frecuentes en las comentarios positivos de la review. Aparece el articulo the.
reviews <- df[sample(nrow(df), 40000), ]
reviews <- reviews[reviews$Positive_Review!='No Positive',]
reviews <- reviews[reviews$Negative_Review!='No Negative',]
term_freq <- function(df,sent){
if(sent=='pos'){
corpus <- Corpus(VectorSource(df$Positive_Review))
}else{
corpus <- Corpus(VectorSource(df$Negative_Review))
}
corpus <- tm_map(corpus, removeWords, stopwords("SMART"))
corpus <- tm_map(corpus, removeWords, stopwords("en"))
corpus <- tm_map(corpus, stripWhitespace)
dtm <-TermDocumentMatrix(corpus)
mat_dtm <- as.matrix(dtm)
v_dtm <- sort(rowSums(mat_dtm),decreasing = TRUE)
FreqMat <- data.frame(word = names(v_dtm), Freq = v_dtm)
FreqMat <- FreqMat[1:50,]
return(FreqMat)
}
wordcloud2(data = term_freq(reviews,'pos'),minRotation = 0,maxRotation = 0)
Se muestran las palabras mas frecuentes en las comentarios positivos de la review. No aparece el articulo the.
reviews <- df[sample(nrow(df), 40000), ]
reviews <- reviews[reviews$Positive_Review!='No Positive',]
reviews <- reviews[reviews$Negative_Review!='No Negative',]
term_freq <- function(df,sent){
if(sent=='pos'){
corpus <- Corpus(VectorSource(df$Positive_Review))
}else{
corpus <- Corpus(VectorSource(df$Negative_Review))
}
corpus <- tm_map(corpus, removeWords, stopwords("SMART"))
corpus <- tm_map(corpus, removeWords, stopwords("en"))
corpus <- tm_map(corpus, stripWhitespace)
dtm <-TermDocumentMatrix(corpus)
mat_dtm <- as.matrix(dtm)
v_dtm <- sort(rowSums(mat_dtm),decreasing = TRUE)
FreqMat <- data.frame(word = names(v_dtm), Freq = v_dtm)
FreqMat <- FreqMat[1:50,]
return(FreqMat)
}
data = term_freq(reviews,'pos')
#print(data)
f <- data["the",]
#print(f)
indexIWant <- which(data[ , 1] == "the") # busco en la primera columna si existe la palabra the
#print(indexIWant)
#print(data[-c(indexIWant),]) # elimina la fila 3 -> the
data <- data[-c(indexIWant),]
#print(data)
wordcloud2(data,minRotation = 0,maxRotation = 0)
Se muestran las palabras mas frecuentes en las comentarios negativos de la review. No aparece el articulo the.
# Original
#wordcloud2(data = term_freq(reviews,'neg'),minRotation = 0,maxRotation = 0)
# Eliminado el articulo the
data = term_freq(reviews,'neg')
#print(data)
f <- data["the",]
#print(f)
indexIWant <- which(data[ , 1] == "the") # busco en la primera columna si existe la palabra the
#print(indexIWant)
#print(data[-c(indexIWant),]) # elimina la fila 3 -> the
data <- data[-c(indexIWant),]
#print(data)
wordcloud2(data,minRotation = 0,maxRotation = 0)
Se muestra un grafico que muestra la distribucion de las puntuaciones medias durante los años que se tienen en la review
nationality <- df %>%
filter(Reviewer_Nationality != " ") %>%
select(Review_year,
Reviewer_Nationality,
Reviewer_Score,
Average_Score,
Total_Number_of_Reviews_Reviewer_Has_Given,
Total_Number_of_Reviews
) %>%
mutate(Reviewer_Nationality = as.character(Reviewer_Nationality)) %>%
group_by(Reviewer_Nationality,Review_year) %>%
summarise(
Averager_Score = mean(Average_Score),
Reviewer_Score = mean(Reviewer_Score),
Total_Number_of_Reviews_Reviewer_Has_Given = sum(Total_Number_of_Reviews_Reviewer_Has_Given),
Total_Number_of_Reviews = n()
) %>%
arrange(Reviewer_Nationality,Review_year)
g <- ggplot(nationality, aes(Averager_Score)) + scale_fill_brewer(palette = "Accent")+ylab("Contador")+xlab("Puntuación media")
g + geom_histogram(aes(fill=Review_year),
binwidth = .1,
col="black", #TODO poner en español
size=.1) + labs(title="Histograma - Distribución anual de las puntuaciones medias")